﻿
    using System;
    using System.Collections.Generic;
    using System.Text;
    using SwinGame;
    using System.Drawing;

    namespace NeuralNetwork.src
    {
        public static class math
        {
            const double _pp = 0.2;

            public static double Sigmoid(double a, double p)
            {
                return (1.00 / (1 + Math.Pow(Math.E, -a / _pp)));
            }

            public static Point V2P(Vector V)
            {
                return new Point((int)V.X, (int)V.Y);
            }

            public static double GetDistanceFromPoint(Point pos, Point a, int size)
            {
                double d = Math.Sqrt((pos.X - a.X) * (pos.X - a.X) + (pos.Y - a.Y) * (pos.Y - a.Y));
                return d - size;
            }

        }


        public class Neuron
        {
            const byte MAX_CONNECTIONS = 64;
            Neuron[] _prevNeurons = new Neuron[MAX_CONNECTIONS];
            double[] _weight = new double[MAX_CONNECTIONS];
            byte _n = 0;
            bool _input = false;
            double _inputValue = 0;

            public double InputValue { get { return _inputValue; } set { _inputValue = value; } }

            public Neuron(bool isInput) { _input = isInput; }

            public void RandomWeights(Random rand)
            {
                for (int i = 0; i < _n; i++)
                {
                    _weight[i] = rand.NextDouble() * 2 - 1;
                }
            }

            public void ConnectFrom(Neuron neuron, Random rand)
            {
                _prevNeurons[_n] = neuron;
                _weight[_n] = rand.NextDouble() * 2 - 1;
                _n++;
            }
            public double GetInput()
            {
                if (!_input)
                {
                    //Sum w[] * i[]
                    double activation = 0.00;
                    for (int i = 0; i < _n; i++)
                    {
                        activation += _weight[i] * _prevNeurons[i].GetInput();
                    }
                    return math.Sigmoid(activation, 1);
                }
                else
                {
                    return _inputValue;
                }

            }

        }
        class NeuralNet
        {
            Neuron[] _input;
            byte _inputN = 0;

            Neuron[,] _net;
            byte _netX = 0;
            byte _netY = 0;

            Neuron[] _output;
            byte _outputN = 0;

            public void Rand(Random rand)
            {
                for (int i = 0; i < _inputN; i++)
                {
                    _input[i].RandomWeights(rand);
                }
                for (int i = 0; i < _outputN; i++)
                {
                    _output[i].RandomWeights(rand);
                }
                for (int x = 0; x < _netX; x++)
                {
                    for (int y = 0; y < _netY; y++)
                    {
                        _net[x, y].RandomWeights(rand);
                    }
                }
            }

            public void SetUp(byte inputs, byte netHeight, byte netWidth, byte outputs, Random rand)
            {
                //set sizes//
                this._inputN = inputs;
                this._netX = netWidth;
                this._netY = netHeight;
                this._outputN = outputs;
                //create neurons//
                _input = new Neuron[_inputN];
                _net = new Neuron[_netX, _netY];
                _output = new Neuron[_outputN];

                for (int i = 0; i < _inputN; i++)
                {
                    _input[i] = new Neuron(true);
                }
                for (int i = 0; i < _netX; i++)
                {
                    for (int x = 0; x < _netY; x++)
                    {
                        _net[i, x] = new Neuron(false);
                    }
                }
                for (int i = 0; i < _outputN; i++)
                {
                    _output[i] = new Neuron(false);
                }
                //connect outputs to last nets//
                for (int i = 0; i < _outputN; i++)
                {
                    for (int x = 0; x < _netY; x++)
                    {
                        _output[i].ConnectFrom(_net[_netX - 1, x],rand);
                    }
                }
                //connect back nets to front nets//
                for (int x = _netX; x > 1; x--)
                {
                    for (int y = _netY; y > 0; y--)
                    {
                        int xa = x - 1; int ya = y - 1;
                        for (int b = 0; b < _netY; b++)
                        {
                            _net[xa, ya].ConnectFrom(_net[xa - 1, b],rand);
                        }
                    }
                }
                //connect first net to inputs//
                for (int y = 0; y < _netY; y++)
                {
                    for (int i = 0; i < _inputN; i++)
                    {
                        _net[0, y].ConnectFrom(_input[i],rand);
                    }
                }
            }

            public NeuralNet GetMutated(int amount, Random rand)
            {
                NeuralNet nn = (NeuralNet)this.MemberwiseClone();
                int chance = 0;
                //input
                chance += _inputN;
                //nn
                chance += _netX * _netY;
                //output
                chance += _outputN;
                for (int i = 0; i < amount; i++)
                {
                    int c = rand.Next(0, chance);
                    if (c < _inputN)
                    {
                        c = rand.Next(1, _inputN);
                        nn._input[c].RandomWeights(rand);
                    }
                    else
                        if (c > _inputN + _netX * _netY)
                        {
                            c = rand.Next(1, _outputN);
                            nn._output[c].RandomWeights(rand);
                        }
                        else
                        {
                            int a = rand.Next(_netX);
                            int b = rand.Next(_netY);
                            nn._net[a, b].RandomWeights(rand);
                        }
                }
                return nn;
            }

            public double[] GetOutput(double[] input)
            {
                for (int i = 0; i < input.Length; i++)
                {
                    this._input[i].InputValue = input[i];
                }
                double[] outp = new double[_outputN];

                for (int i = 0; i < _outputN; i++)
                {
                    outp[i] = this._output[i].GetInput();
                }

                return outp;
            }
        }
    }